package com.smart.community.commons.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 服务调用监控切面
 * 
 * 监控Feign接口调用情况，记录调用次数、成功率、响应时间等指标
 * 提供详细的调用统计信息，便于性能监控和问题排查
 * 
 * @author Wu.Liang
 * @version 2.0.0
 * @since 2025-08-28
 */
@Aspect
@Component
@Slf4j
public class FeignCallMonitorAspect {
    
    /**
     * 调用统计缓存
     * key: 服务名.方法名
     * value: 调用统计信息
     */
    private static final ConcurrentHashMap<String, CallStatistics> callStatisticsMap = new ConcurrentHashMap<>();
    
    /**
     * 监控Feign接口调用
     */
    @Around("@annotation(org.springframework.cloud.openfeign.FeignClient)")
    public Object monitorFeignCall(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        String className = joinPoint.getTarget().getClass().getSimpleName();
        String serviceKey = className + "." + methodName;
        long startTime = System.currentTimeMillis();
        
        try {
            Object result = joinPoint.proceed();
            long duration = System.currentTimeMillis() - startTime;
            
            log.debug("Feign调用成功 - 类: {}, 方法: {}, 耗时: {}ms", className, methodName, duration);
            
            // 记录成功指标
            recordSuccessMetrics(serviceKey, duration);
            
            return result;
        } catch (Exception e) {
            long duration = System.currentTimeMillis() - startTime;
            
            log.warn("Feign调用失败 - 类: {}, 方法: {}, 耗时: {}ms, 错误: {}", 
                    className, methodName, duration, e.getMessage());
            
            // 记录失败指标
            recordFailureMetrics(serviceKey, duration, e);
            
            throw e;
        }
    }
    
    /**
     * 记录成功指标
     * 
     * @param serviceKey 服务键名
     * @param duration 调用耗时
     */
    private void recordSuccessMetrics(String serviceKey, long duration) {
        CallStatistics stats = callStatisticsMap.computeIfAbsent(serviceKey, k -> new CallStatistics());
        stats.recordSuccess(duration);
        
        // 记录详细的成功统计信息
        log.debug("服务调用统计 - {}: 总调用={}, 成功={}, 失败={}, 成功率={:.2f}%, 平均耗时={:.2f}ms", 
                serviceKey, 
                stats.getTotalCalls(), 
                stats.getSuccessCalls(), 
                stats.getFailureCalls(), 
                stats.getSuccessRate() * 100, 
                stats.getAverageDuration());
    }
    
    /**
     * 记录失败指标
     * 
     * @param serviceKey 服务键名
     * @param duration 调用耗时
     * @param e 异常信息
     */
    private void recordFailureMetrics(String serviceKey, long duration, Exception e) {
        CallStatistics stats = callStatisticsMap.computeIfAbsent(serviceKey, k -> new CallStatistics());
        stats.recordFailure(duration);
        
        // 记录详细的失败统计信息
        log.warn("服务调用失败统计 - {}: 总调用={}, 成功={}, 失败={}, 成功率={:.2f}%, 平均耗时={:.2f}ms, 错误类型: {}", 
                serviceKey, 
                stats.getTotalCalls(), 
                stats.getSuccessCalls(), 
                stats.getFailureCalls(), 
                stats.getSuccessRate() * 100, 
                stats.getAverageDuration(),
                e.getClass().getSimpleName());
        
        // 触发告警通知（可以扩展为发送邮件、短信等）
        triggerAlert(serviceKey, e);
    }
    
    /**
     * 触发告警通知
     * 
     * @param serviceKey 服务键名
     * @param e 异常信息
     */
    private void triggerAlert(String serviceKey, Exception e) {
        // 这里可以实现具体的告警逻辑
        // 例如：发送邮件、短信、钉钉消息等
        
        // 示例：记录告警日志
        log.error("服务调用告警 - 服务: {}, 异常: {}, 时间: {}", 
                serviceKey, e.getMessage(), System.currentTimeMillis());
        
        // TODO: 实现具体的告警通知逻辑
        // 1. 发送邮件通知
        // 2. 发送短信通知
        // 3. 发送钉钉/企业微信消息
        // 4. 记录到告警数据库
    }
    
    /**
     * 获取调用统计信息
     * 
     * @param serviceKey 服务键名
     * @return 调用统计信息
     */
    public static CallStatistics getCallStatistics(String serviceKey) {
        return callStatisticsMap.get(serviceKey);
    }
    
    /**
     * 获取所有调用统计信息
     * 
     * @return 所有调用统计信息
     */
    public static ConcurrentHashMap<String, CallStatistics> getAllCallStatistics() {
        return callStatisticsMap;
    }
    
    /**
     * 清除调用统计信息
     * 
     * @param serviceKey 服务键名
     */
    public static void clearCallStatistics(String serviceKey) {
        callStatisticsMap.remove(serviceKey);
    }
    
    /**
     * 清除所有调用统计信息
     */
    public static void clearAllCallStatistics() {
        callStatisticsMap.clear();
    }
    
    /**
     * 调用统计信息类
     */
    public static class CallStatistics {
        private final AtomicLong totalCalls = new AtomicLong(0);
        private final AtomicLong successCalls = new AtomicLong(0);
        private final AtomicLong failureCalls = new AtomicLong(0);
        private final AtomicLong totalDuration = new AtomicLong(0);
        
        /**
         * 记录成功调用
         * 
         * @param duration 调用耗时
         */
        public void recordSuccess(long duration) {
            totalCalls.incrementAndGet();
            successCalls.incrementAndGet();
            totalDuration.addAndGet(duration);
        }
        
        /**
         * 记录失败调用
         * 
         * @param duration 调用耗时
         */
        public void recordFailure(long duration) {
            totalCalls.incrementAndGet();
            failureCalls.incrementAndGet();
            totalDuration.addAndGet(duration);
        }
        
        /**
         * 获取总调用次数
         * 
         * @return 总调用次数
         */
        public long getTotalCalls() {
            return totalCalls.get();
        }
        
        /**
         * 获取成功调用次数
         * 
         * @return 成功调用次数
         */
        public long getSuccessCalls() {
            return successCalls.get();
        }
        
        /**
         * 获取失败调用次数
         * 
         * @return 失败调用次数
         */
        public long getFailureCalls() {
            return failureCalls.get();
        }
        
        /**
         * 获取成功率
         * 
         * @return 成功率（0.0-1.0）
         */
        public double getSuccessRate() {
            long total = totalCalls.get();
            if (total == 0) {
                return 0.0;
            }
            return (double) successCalls.get() / total;
        }
        
        /**
         * 获取平均调用耗时
         * 
         * @return 平均调用耗时（毫秒）
         */
        public double getAverageDuration() {
            long total = totalCalls.get();
            if (total == 0) {
                return 0.0;
            }
            return (double) totalDuration.get() / total;
        }
        
        /**
         * 获取总调用耗时
         * 
         * @return 总调用耗时（毫秒）
         */
        public long getTotalDuration() {
            return totalDuration.get();
        }
        
        @Override
        public String toString() {
            return String.format("CallStatistics{totalCalls=%d, successCalls=%d, failureCalls=%d, successRate=%.2f%%, averageDuration=%.2fms}", 
                    getTotalCalls(), getSuccessCalls(), getFailureCalls(), getSuccessRate() * 100, getAverageDuration());
        }
    }
}
